package p.ithorns.tools.log;

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import p.ithorns.framework.core.model.Result;
import p.ithorns.framework.common.utils.IPUtil;
import p.ithorns.framework.common.utils.JsonUtil;
import p.ithorns.framework.common.thread.SystemClock;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.Objects;

/**
 * RequestLogAspect 请求日志
 *
 * @author ithorns
 * @version 1.0
 * @date 2021/4/2 18:40
 */
@Order(10)
@Aspect
@Component
@ConditionalOnClass(name = "org.aspectj.lang.ProceedingJoinPoint")
public class ReqLogAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger(ReqLogAspect.class);

    private final static String REQ_TYPE_FILE = "file";

    @Pointcut("execution(* ..controller..*(..))")
    public void controller() {
    }


    @Around("controller()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        RequestInfo request = resolve(pjp);

        //记录请求开始执行时间：
        long beginTime = SystemClock.now();
        Object result;
        try {
            result = pjp.proceed();
        } catch (Throwable throwable) {
            //请求操纵失败 记录错误日志
            request.setResponse(throwable.getMessage());
            LOGGER.info("Request<EX>|==> 请求错误! {}", request);
            throw throwable;
        }
        //记录请求完成执行时间：
        long endTime = SystemClock.now();
        long usedTime = endTime - beginTime;

        //请求操作成功
        String resultJson = "";
        if (result != null) {
            if (result instanceof Result) {
                resultJson = JsonUtil.toJson(result);
            } else {
                resultJson = String.valueOf(result);
            }
        }

        // 控制显示长度
        if (null != resultJson && resultJson.length() > 1024) {
            resultJson = resultJson.substring(0, 1024) + "...";
        }
        request.setResponse(resultJson);

        //记录日志
        LOGGER.info("Request|==> 请求成功! 耗时:[{}ms].\n{}", usedTime, request);

        // TODO Save to DB

        return result;
    }

    private RequestInfo resolve(ProceedingJoinPoint pjp) {
        //获取请求信息
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(sra).getRequest();

        //获取代理地址、请求地址、请求类名、方法名
        //获取请求参数
        MethodSignature ms = (MethodSignature) pjp.getSignature();
        //获取请求参数类型
        String[] parameterNames = ms.getParameterNames();
        //获取请求参数值
        Object[] parameterValues = pjp.getArgs();
        StringBuilder params = new StringBuilder();
        //组合请求参数，进行日志打印
        if (parameterNames != null && parameterNames.length > 0) {
            params.append("{");
            for (int i = 0; i < parameterNames.length; i++) {
                String name = parameterNames[i];
                if ("bindingResult".equals(name)) {
                    break;
                }

                // 文件
                if (REQ_TYPE_FILE.equals(name.toLowerCase())) {
                    continue;
                }

                Object value = parameterValues[i];
                if ((value instanceof HttpServletRequest) || (value instanceof HttpServletResponse)) {
                    params.append(name).append(":").append(value).append(",");
                } else {
                    params.append(name).append(":")
                            .append(JsonUtil.toJson(value))
                            .append(",");
                }
            }
            // 防止String index out of range: -1
            int index = params.lastIndexOf(",");
            if (index != -1) {
                params.deleteCharAt(index);
            }
            params.append("}");
        }

        final RequestInfo.RequestInfoBuilder builder = RequestInfo.builder()
                .date(LocalDateTime.now().toString())
//                .client(request.getHeader(GlobalNames.HEADER_CLIENT_ID))
//                .secret(request.getHeader(GlobalNames.HEADER_CLIENT_SECRET))
                .address(IPUtil.getClientIp(request))
                .uri(request.getRequestURI())
                .method(request.getMethod())
                .clazz(pjp.getTarget().getClass().getSimpleName() + "->" + pjp.getSignature().getName())
                .params(params.toString());

//        Optional.ofNullable(UserContext.getUser())
//                .ifPresent(user -> builder.user(user.getUserName()));

        return builder.build();
    }

    @Getter
    @Setter
    @Builder
    static class RequestInfo {
        String client;
        String secret;
        String user;
        String address;
        String uri;
        String clazz;
        String method;
        String params;
        String response;
        String date;

        @Override
        public String toString() {
            return String.format(
                    "%s %s\r\n==>|PARAMS：%s\r\n==>|IP: %s\r\n==>|CLIENT: %s\r\n==>|SECRET: %s\r\n==>|USER: %s\r\n==>|CONTROLLER：%s\r\n==>|DATE: %s\r\n==>|RESPONSE: %s",
                    method, uri, params, address, client, secret, user, clazz, date, response
            );
        }
    }

}
